
#include "AAudio.h"
#include "AXML.h"
#include "AString.h"
#include "AUserData.h"

#define STATE_IDLE		0x000
#define STATE_PLAYING	0x001
#define STATE_PAUSING	0x002
#define STATE_RESUMING	0x003
#define STATE_SWITCHING	0x004
#define STATE_STOPPING	0x005
#define STATE_PLAYIDLE	0x006

//float _volumeMaxMusic;
//float _volumeMaxSound;

CAAudio::CAAudio(CATimeProvider* ptp)
{
	_timer = ptp;

	this->_aryMusic = CCArray::array();
	this->_aryMusic->retain();
	this->_arySound = CCArray::array();
	this->_arySound->retain();

	_playing = null;
	_toplay = null;
	//_volumeMaxMusic = 1.0f;
	//_volumeMaxSound = 1.0f;
	//_volumeMusic = 1.0f;
	//_volumeSound = 1.0f;
	_volumeMusic = CAUserData::sharedUserData().getFloat("vol_music", 1.0f);
	_volumeSound = CAUserData::sharedUserData().getFloat("vol_sound", 1.0f);

	_FADE_SWITCH	= 1.7f;
	_FADE_PAUSE		= 0.5f;
	_FADE_RESUME	= 0.5f;

	_setState(STATE_IDLE);

	autorelease();
}

CAAudio::~CAAudio(void)
{
	this->_aryMusic->release(); this->_aryMusic = null;
	this->_arySound->release(); this->_arySound = null;
}

void CAAudio::loadMusicItem(CAXMLNode* pnode)
{
	GUARD_FUNCTION();

	CAMusicItem* pitem = new CAMusicItem();

	pitem->id = pnode->attr("id");
	pitem->file = RES(pnode->attr("src").c_str());
	pitem->playtime = -1;
	CAStringUtils::toFloat(pnode->attr("duration"), pitem->duration);
	_Assert(pitem->duration > 3.0f);

	SimpleAudioEngine::sharedEngine()->
		preloadBackgroundMusic(RES(pitem->file.c_str()));

	_aryMusic->addObject(pitem);
}

void CAAudio::loadSoundItem(CAXMLNode* pnode)
{
	GUARD_FUNCTION();

	CASoundItem* pitem = new CASoundItem();

	pitem->id = pnode->attr("id");
	pitem->file = RES(pnode->attr("src").c_str());

	SimpleAudioEngine::sharedEngine()->
		preloadEffect((pitem->file.c_str()));

	_arySound->addObject(pitem);
}

CAAudio::CASoundItem* CAAudio::_findItem(CCArray* pary, const char* pszMusicOrSound, int* pnIndex)
{
	size_t c = pary->count();
	size_t i;
	for (i = 0; i < c; i++)
	{
		CASoundItem* pitem = (CASoundItem*)pary->objectAtIndex(i);
		if (pitem->id == pszMusicOrSound)
		{
			if (null != pnIndex)
			{
				*pnIndex = i;
			}
			return pitem;
		}
	}
	return null;
}

void CAAudio::_setState(int st)
{
	_state = st;
	_stateTime = _timer->getTimeNow();
}

void CAAudio::switchMusic(const char* pszMusic)
{
	CAMusicItem* pitem = null;
	pitem = (CAMusicItem*)_findItem(_aryMusic, pszMusic);
	if (null == pitem)
	{
		return;
	}
	_toplay = pitem;
	_setState(STATE_SWITCHING);
}

void CAAudio::playMusic()
{
	if (null != _playing && _aryMusic->count() <= 0)
		return;

	_setState(STATE_PLAYING);
}

void CAAudio::stopMusic()
{
	if (null != _playing && _aryMusic->count() <= 0)
		return;

	_setState(STATE_STOPPING);
}

void CAAudio::pauseMusic()
{
	if (null != _playing)
		return;
	if (STATE_PLAYING != _state)
		return;
	_setState(STATE_PAUSING);
}

void CAAudio::resumeMusic()
{
	if (null == _playing)
		return;
	if (STATE_PLAYIDLE != _state)
		return;
	_Assert(null != _playing);
	SimpleAudioEngine::sharedEngine()->resumeBackgroundMusic();
	_setState(STATE_RESUMING);
}

void CAAudio::playSound(const char* pszSound, float volume)
{
	CASoundItem* pitem = (CASoundItem*)_findItem(_arySound, pszSound);
	if (pitem)
	{
		SimpleAudioEngine::sharedEngine()->setEffectsVolume(_volumeSound * volume);
		SimpleAudioEngine::sharedEngine()->playEffect((pitem->file.c_str()));
	}
}

#define FADE_PRECISION (10.0f)
bool CAAudio::_fadeoutMusic(float intime)
{
	if (null == _playing)
		return false;

	float delta = _timer->getTimeNow() - _stateTime;
	_Assert(delta >= 0.0f);
	float fac = delta / intime;
	if (fac > 1.0f) fac = 1.0f;

	int vol = (int)(0.5f + FADE_PRECISION * (1.0f - fac) * _volumeMusic);
	int volOld = (int)(0.5f + FADE_PRECISION * SimpleAudioEngine::sharedEngine()->getBackgroundMusicVolume());
	if (vol != volOld)
	{
		//_Trace("fadeout volume=%.3f, lastvol=%.3f _stateTime=%f", vol / FADE_PRECISION, volOld / FADE_PRECISION, _stateTime);
		SimpleAudioEngine::sharedEngine()->setBackgroundMusicVolume(vol / FADE_PRECISION);
		//test
		//volOld = (int)(FADE_PRECISION * SimpleAudioEngine::sharedEngine()->getBackgroundMusicVolume());
		//float f = vol / FADE_PRECISION;
		//int fn = (int)(FADE_PRECISION * f);
		//_Assert(volOld == vol);
	}

	return vol > 0.0f;
}

bool CAAudio::_fadeinMusic(float intime)
{
	_Assert(null != _playing);

	float delta = _timer->getTimeNow() - _stateTime;
	_Assert(delta >= 0.0f);
	float fac = delta / intime;
	if (fac > 1.0f) fac = 1.0f;
	
	//note:
	// float v = 7 / FADE_PRECISION;		==> v = 0.69999999999.....
	// int vn = (int)(FADE_PRECISION * v);	==> vn = 6

	int vol = (int)(0.5f + FADE_PRECISION * (1.0f - fac) * _volumeMusic);
	int volOld = (int)(0.5f + FADE_PRECISION * SimpleAudioEngine::sharedEngine()->getBackgroundMusicVolume());
	if (vol != volOld)
	{
		//_Trace("fadein volume=%.3f, lastvol=%.3f _stateTime=%f", vol / FADE_PRECISION, volOld / FADE_PRECISION, _stateTime);
		SimpleAudioEngine::sharedEngine()->setBackgroundMusicVolume(vol / FADE_PRECISION);
		//test
		//volOld = (int)(FADE_PRECISION * SimpleAudioEngine::sharedEngine()->getBackgroundMusicVolume());
		//float f = vol / FADE_PRECISION;
		//int fn = (int)(FADE_PRECISION * f);
		//_Assert(volOld == vol);
	}


	return vol < _volumeMusic;
}

void CAAudio::update()
{
	switch (_state)
	{
	case STATE_IDLE:
		break;
	case STATE_PLAYIDLE:
		break;
	case STATE_SWITCHING:
		{
			if (null != _toplay) //fade out first
			{
				if (_fadeoutMusic(_FADE_SWITCH))
				{
				}
				else
				{
					//fadeout finished
					SimpleAudioEngine::sharedEngine()->playBackgroundMusic(_toplay->file.c_str());
					_playing = _toplay;
					_toplay = null;
					_playing->playtime = _timer->getTimeNow();
					//reset _stateTime
					_stateTime = _timer->getTimeNow();
				}
			}
			else //fade in playing music
			{
				if (_fadeinMusic(_FADE_SWITCH))
				{
				}
				else
				{
					//fadein finished
					_setState(STATE_PLAYING);
				}
			}
		}
		break;
	case STATE_PAUSING:
		if (_fadeoutMusic(_FADE_PAUSE))
		{
		}
		else
		{
			//fadeout finished
			SimpleAudioEngine::sharedEngine()->pauseBackgroundMusic();
			_setState(STATE_PLAYIDLE);
		}
		break;
	case STATE_RESUMING:
		if (_fadeinMusic(_FADE_RESUME))
		{
		}
		else
		{
			_setState(STATE_PLAYING);
		}
		break;

	case STATE_PLAYING:
		{
			int index = -1;
			if (null == _playing)
			{
				index = 0;
			}
			else if (_playing->playtime + _playing->duration - _FADE_SWITCH < _timer->getTimeNow())
			{
				_findItem(_aryMusic, _playing->id.c_str(), &index);
				index++;
				_Assert(_aryMusic->count() > 0);
				index %= _aryMusic->count();
			}
			if (index >= 0 && index < (int)_aryMusic->count())
			{
				CAMusicItem* pitem = (CAMusicItem*)_aryMusic->objectAtIndex(index);
				_toplay = pitem;
				_setState(STATE_SWITCHING);
			}
		}
		break;
	case STATE_STOPPING:
		if (_fadeoutMusic(_FADE_PAUSE))
		{
		}
		else
		{
			//fadeout finished
			SimpleAudioEngine::sharedEngine()->stopBackgroundMusic();
			_playing = _toplay = null;
			_setState(STATE_IDLE);
		}
		break;
	default:
		_Assert(false);
		break;
	}
}

void CAAudio::enableMusic(bool e)
{ 
	if (e) 
	{
		_volumeMusic = 1.0f; 
		resumeMusic();
	}
	else 
	{
		_volumeMusic = 0.0f; 
		pauseMusic();
	}
	CAUserData::sharedUserData().setFloat("vol_music", _volumeMusic);
}
;
void CAAudio::enableSound(bool e)
{ 
	if (e) 
		_volumeSound = 1.0f; 
	else 
		_volumeSound = 0.0f; 
	CAUserData::sharedUserData().setFloat("vol_sound", _volumeSound);
};
